package allenja.cp2011.MVC;

import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Scanner;

public class MessageModel extends Observable {
	private ServerSocket serverSocket;
	private Thread listenerThread;
	private ArrayList<Thread> connectionThreads;
	private boolean running, connected;
	private int portNumber;
	private int MAXCONNECTIONS = 100;
	private MessageHandler messageHandler = new MessageHandler();
	private Rot13EncryptDecrypt rot13Helper = new Rot13EncryptDecrypt();
	
	public MessageModel() {
		connectionThreads = new ArrayList<Thread>();
	}
	
	// Set the port number
	public void setPortNumber(int number) {
		portNumber = number;
	}

	// Validate the port number
	private boolean invalidPortNumber() {
		return portNumber < 49152 || portNumber > 65535;
	}

	// Start the Server Application and Client listener threads
	public void start() {
		setChanged();
		notifyObservers("Server Starting...");
		
		// Validate the port number
		if (running || invalidPortNumber())	{
			setChanged();
			notifyObservers("MessageServer: Running already or invalid port number");
			return;
		}

		listenerThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					setChanged();
					notifyObservers("Listener Started...");
					serverSocket = new ServerSocket(portNumber);
					running = true;

					// Listen for client connections,start connection threads and throttle client connections to less than 100 (MAXCONNECTIONS)
					while (running && connectionThreads.size() < MAXCONNECTIONS) {
						setChanged();
						notifyObservers("Waiting for connection...");
						final Socket connectionSocket = serverSocket.accept();
						
						setChanged();
						notifyObservers("Got a new connection!");

						Thread connectionThread = new Thread(new Runnable() {
							
							@Override
							public void run() {
								try {
									// Create the input and output streams
									Scanner reader = new Scanner(connectionSocket.getInputStream());
									PrintWriter writer = new PrintWriter(connectionSocket.getOutputStream());

									connected = true;
									
									// Handle client requests
									while (running && connected) {
										setChanged();
										notifyObservers("Waiting for request..");

										String requestString = rot13Helper.convert(reader.nextLine());

										setChanged();
										notifyObservers("Received: " + requestString);
										
										if (requestString.equals("END")) {
											connected = false;
											setChanged();
											notifyObservers("Connection closing...");
											break;
										}
										
										// Pass the response to request handler
										String response = messageHandler.handleRequest(requestString);

										if (response != null) {
											writer.println(response);
											writer.flush();
										} else {
											writer.println("NONE");
											writer.flush();
										}
									}									
								} catch (Exception e) {
									System.out.println("Connection thread error: " + e);
								} finally {
									connected = false;
									try {
										connectionSocket.close();
									} catch (Exception e) {}
								}
							}
						});
						connectionThreads.add(connectionThread);
						connectionThread.start();
					}
				} catch (Exception e) {
					setChanged();
					notifyObservers("Listener Thread closed");
				} finally {
					stop();
				}
			}
		});
		listenerThread.start();
	}

	// Stop the listener threads and connection threads
	public void stop() {
		if (!running) return;
		
		setChanged();
		notifyObservers("Stopping message stream...");

		running = false;
		connected = false;
		
		for (Thread thread : connectionThreads) {
			thread.interrupt();
		}
		
		listenerThread.interrupt();
		
		try {
			serverSocket.close();
		} catch (Exception e) {}
	}
}
